/**
 * @file tts_bridge.cpp
 * @brief C++到C的文本转语音(TTS)桥接实现
 * @details 该文件使用讯飞星火的C++ SDK实现TTS功能，并通过 extern "C" 接口
 * 将其封装成C语言可以调用的函数。这使得C项目能够方便地使用基于C++的SDK。
 * @version 1.0
 * @date 2025-07-07
 */

#include <iostream>
#include <string>
#include <atomic>   // 用于线程安全的原子操作 (atomic_bool)
#include <fstream>
#include <unistd.h> // 提供 sleep() 函数
#include <stdio.h>
#include <stdlib.h> // 提供 remove() 函数

// --- 讯飞 SDK 头文件 ---
#include "sparkchain.h"
#include "sc_tts.h"

// --- 桥接头文件 ---
// 包含此文件中C接口函数的声明，以确保实现与声明一致
#include "tts_bridge.h"

// 使用标准库和讯飞SDK的命名空间，简化代码
using namespace SparkChain;
using namespace std;

// =====================================================================
// --- 全局区域 ---
// =====================================================================

/**
 * @brief TTS异步合成完成的标志
 * @details SDK的回调函数在不同的线程中执行，主线程需要等待合成完成。
 * 使用 std::atomic_bool 可以确保在多线程环境下的读写操作是原子的，
 * 避免了数据竞争问题，无需手动加锁。
 */
static atomic_bool tts_finish(false);

// SDK 初始化参数 (注意：请务必替换为您自己的讯飞星火大模型应用的凭证)
const char *APPID     = "xxxxxxxxx";
const char *APIKEY    = "xxxxxxxxxx";
const char *APISECRET = "xxxxxxxxxxx";
const char *WORKDIR   = "./"; // SDK工作目录，用于存放日志等文件
int logLevel          = 100;  // 日志级别，100 表示关闭日志输出

// =====================================================================
// --- 核心C++组件 (基于讯飞SDK的实现) ---
// =====================================================================

/**
 * @class onlineTTSCallbacks
 * @brief 实现了讯飞TTS SDK的回调接口
 * @details SDK的TTS功能是异步的。当有音频数据生成或发生错误时，SDK会调用
 * 这个类中对应的方法。
 */
class onlineTTSCallbacks : public TTSCallbacks {
public:
    /**
     * @brief 当收到TTS合成的音频数据时被调用
     * @param result 包含音频数据、长度和状态的结果对象
     * @param usrTag 用户在调用arun时传入的自定义数据，这里我们用它来传递输出文件路径
     */
    void onResult(TTSResult * result, void * usrTag) override {
        // 将用户自定义数据转回它本来的类型 (文件路径字符串)
        const char* currentAudioPath = static_cast<const char*>(usrTag);
        const char * data = result->data(); // 获取音频数据指针
        int len           = result->len();  // 获取数据长度
        int status        = result->status(); // 获取当前状态 (0:合成中, 1:合成中, 2:合成结束)

        // 以二进制追加模式("ab")打开文件，并将收到的数据块写入文件
        FILE* file = fopen(currentAudioPath, "ab");
        if (file) {
            fwrite(data, 1, len, file);
            fclose(file);
        }

        // 当状态为2时，表示整个TTS流程已完成
        if (status == 2) {
            // 设置全局完成标志为true，通知主线程可以继续执行
            tts_finish = true;
        }
    }

    /**
     * @brief 当TTS请求发生错误时被调用
     * @param error 包含错误码和错误信息的对象
     * @param usrTag 用户自定义数据
     */
    void onError(TTSError * error, void * usrTag) override {
        printf("TTS 请求出错, 错误码: %d, 错误信息:%s\n", error->code(), error->errMsg().c_str());
        // 发生错误时，同样需要设置完成标志，以解除主线程的等待
        tts_finish = true;
    }
};

/**
 * @brief 内部使用的C++ TTS实现函数
 * @param text 要转换为语音的文本
 * @param outputPath 生成的音频文件的保存路径
 * @param vcn 使用的发音人 (例如 "xiaoyan", "aisjiuxu")
 * @return bool 成功返回true，失败返回false
 */
bool Text_to_TTS_internal(const char* text, const char* outputPath, const char* vcn = "xiaoyan") {
    // 1. 创建并配置TTS实例
    OnlineTTS tts(vcn);    // 使用指定发音人创建TTS对象
    tts.aue("lame");       // 设置音频编码为lame，即输出MP3格式
    onlineTTSCallbacks cbs{}; // 创建回调处理对象
    tts.registerCallbacks(&cbs); // 注册回调

    // 2. 准备开始合成
    remove(outputPath);      // 删除可能已存在的同名旧文件
    tts_finish = false;      // 重置完成标志

    printf("开始合成: \"%s\" -> %s\n", text, outputPath);

    // 3. 发起异步合成请求
    // arun 是异步执行函数，会立即返回。它将任务提交给SDK后台处理。
    // 我们将 outputPath 作为用户数据(usrTag)传递，以便在回调函数中知道要写入哪个文件。
    int ret = tts.arun(text, (void*)outputPath);
    if (ret != 0) {
        printf("TTS 合成请求发送失败, 错误码: %d\n", ret);
        return false;
    }

    // 4. 等待异步任务完成
    // 这是一个简单的轮询等待机制。主线程在这里循环等待，直到回调函数将 tts_finish 设置为 true。
    int times = 0;
    while (!tts_finish) {
        sleep(1); // 每秒检查一次
        // 设置一个超时（例如20秒），防止因网络问题等导致程序无限等待
        if (times++ > 20) {
            printf("TTS 合成超时！\n");
            return false;
        }
    }
    printf("TTS 合成流程结束。\n");
    return true;
}


// =====================================================================
// --- C 桥接函数的实现 ---
// =====================================================================

/**
 * @brief extern "C" 指示编译器使用C语言的调用约定来编译这部分代码
 * @details 这可以防止C++编译器的"名字修饰"(Name Mangling)，从而使得
 * 这些函数能够被C语言代码直接链接和调用。
 */
extern "C" {

/**
 * @brief (C接口) 初始化讯飞TTS SDK
 * @return int 成功返回0，失败返回-1
 */
int init_tts_sdk_c() {
    // 使用建造者模式配置SDK
    SparkChainConfig *config = SparkChainConfig::builder();
    config->appID(APPID)->apiKey(APIKEY)->apiSecret(APISECRET)->workDir(WORKDIR)->logLevel(logLevel);

    // 初始化SDK
    int ret = SparkChain::init(config);
    if (ret != 0) {
        printf("讯飞 SparkChain SDK 初始化失败! 错误码: %d\n", ret);
        return -1; // C语言通常用非0值表示错误
    }
    printf("讯飞 SparkChain SDK 初始化成功。\n");
    return 0; // C语言通常用0表示成功
}

/**
 * @brief (C接口) 释放讯飞TTS SDK资源
 */
void uninit_tts_sdk_c() {
    SparkChain::unInit();
    printf("讯飞 SparkChain SDK 已释放。\n");
}

/**
 * @brief (C接口) 将文本转换为语音文件
 * @param text 要转换的文本 (UTF-8编码)
 * @param outputPath 输出的MP3文件路径
 * @return int 成功返回0，失败返回-1
 */
int text_to_speech_c(const char* text, const char* outputPath) {
    // 调用内部的C++核心函数来完成实际工作
    // 这里可以方便地切换发音人等参数，而无需修改C接口
    bool success = Text_to_TTS_internal(text, outputPath, "aisjiuxu"); // 可选发音人：xiaoyan, xiaoqi, aisjiuxu等
    
    // 将C++的bool值转换为C语言常用的int返回值
    return success ? 0 : -1;
}

} // end of extern "C"

// 原始的main函数可以被注释或删除，因为此文件现在作为一个库来使用。
// 保留它可作为一个独立的测试入口。
/*
int main(int argc, char const *argv[]) {
    if (init_tts_sdk_c() != 0) {
        return -1;
    }

    text_to_speech_c("你好，世界。", "./hello_world.mp3");
    text_to_speech_c("我是讯飞星火认知大模型，很高兴为您服务。", "./spark_intro.mp3");

    uninit_tts_sdk_c();
    return 0;
}
*/